import * as THREE from "three";
import { Shelf, CargoPoint } from "./Shelf";
import { KDTree } from "../lib/kd-tree";
import { type SketchBase, Updatable, singleInstance } from "fast-three-sketch";

class _ShelfManager extends Updatable {

    shelfList: Shelf[];

    private map: Map<string, CargoPoint>;

    private tree: KDTree<THREE.Vector3>;

    constructor () {

        super();

        this.tree = undefined;

        this.shelfList = [];

        this.map = new Map();

    }

    addShelf ( shelf: Shelf ) {

        this.shelfList.push( shelf );

    }

    getVKey ( vec3: THREE.Vector3 ) {
        return `[${ vec3.x },${ vec3.y },${ vec3.z }]`;
    }

    getCargoPoint ( vec3: THREE.Vector3 ) {

        const vKey = this.getVKey( vec3 );

        return this.map.get( vKey );
    }

    updateTree () {

        this.map.clear();

        const positions: THREE.Vector3[] = [];

        for ( let i = 0; i < this.shelfList.length; i++ ) {
            const shelf = this.shelfList[ i ];
            const cargoPoints = shelf.cargoPoints;

            for ( let i = 0; i < cargoPoints.length; i++ ) {

                const cargoPoint = cargoPoints[ i ];

                if ( cargoPoint.isEmpty ) {
                    const vector3 = cargoPoint.object.getWorldPosition( new THREE.Vector3() );

                    const vKey = this.getVKey( vector3 );
                    positions.push( vector3 );
                    this.map.set( vKey, cargoPoint );

                }

            }
        }

        this.tree = new KDTree( positions, ( a, b ) => {
            const dx = a.x - b.x, dy = a.y - b.y, dz = a.z - b.z;
            return Math.sqrt( dx * dx + dy * dy + dz * dz );
        }, [ "x", "y", "z" ] );

    }

    nearestCargoPoint ( point: THREE.Vector3, distance: number ) {
        return this.tree.nearest( point, 1, distance );
    }

    update ( sketch: SketchBase ): void {

        const shelfList = this.shelfList;

        for ( let i = 0; i < shelfList.length; i++ ) {

            const shelf = shelfList[ i ];

            shelf.update( sketch.getDelta() );

        }


    }

}

const ShelfManager = singleInstance( _ShelfManager );

export const shelfManager = new ShelfManager();
